package com.etone.smartAudit.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.XML;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.word.Word07Writer;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.etone.smartAudit.domain.*;
import com.etone.smartAudit.dto.admin.AdminDTO;
import com.etone.smartAudit.dto.auditRisk.AuditRiskDTO;
import com.etone.smartAudit.dto.fieldCase.FieldCaseDTO;
import com.etone.smartAudit.dto.iapDataModel.IapDataModelDTO;
import com.etone.smartAudit.dto.iapPlatform.IapPlatformDTO;
import com.etone.smartAudit.dto.projectFile.*;
import com.etone.smartAudit.enums.ProjectFileLogType;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.mapper.AdminMapper;
import com.etone.smartAudit.mapper.ProjectFileMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.etone.smartAudit.security.SecurityUtils;
import com.etone.smartAudit.utils.Md5Util;
import com.etone.smartAudit.vo.FileVO;
import com.etone.smartAudit.vo.OfficeDetailVo;
import com.etone.smartAudit.vo.OfficeVo;
import com.etone.smartAudit.vo.PagerVO;
import com.etone.smartAudit.vo.admin.AdminListVO;
import com.etone.smartAudit.vo.admin.AdminVO;
import com.etone.smartAudit.vo.adminLog.AdminLogVO;
import com.etone.smartAudit.vo.iapDataModel.IapDataModelBaseVO;
import com.etone.smartAudit.vo.iapPlatform.IapPlatformListVO;
import com.etone.smartAudit.vo.item.ItemSimpleListVO;
import com.etone.smartAudit.vo.item.ItemTypeCntVO;
import com.etone.smartAudit.vo.officefile.FileMode;
import com.etone.smartAudit.vo.projectFile.ProjectDetail;
import com.etone.smartAudit.vo.projectFile.ProjectFileDirVO;
import com.etone.smartAudit.vo.projectFile.ProjectFileVO;
import com.etone.smartAudit.vo.serverInfo.ServerInfoTotalVO;
import com.etone.smartAudit.vo.serviceExpert.ServiceExpertBaseVO;
import com.etone.smartAudit.vo.task.MyTaskAnalysis;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.swagger.annotations.ApiModelProperty;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 项目文件表 项目文件表 服务实现类
 * </p>
 *
 * @author jiayen
 * @since 2021-05-08
 */
@Service
public class ProjectFileService extends ServiceImpl<ProjectFileMapper, ProjectFile> {

    @Value("${wopi.url}")
    private String wopiUrl;


    @Value("${only_office.url:localhost}")
    private String officeUrl;

    @Value("${wopi.digst}")
    private String digst;

    @Value("${wopi.server}")
    private String wopiServer;

    @Value("${audit.filePath}")
    private String filePath;

    @Autowired
    private OfficeFileService officeFileService;

    @Autowired
    private ProjectFileVersionService versionService;

    @Autowired
    private AdminPermissionService adminPermissionService;

    @Autowired
    private ProjectFileCollectService collectService;
    /****
     *  0 - 文件夹，1-文件 ，2-压缩包
     */
    public static final int IS_FILE_TYPE_NO = 0;
    public static final int IS_FILE_TYPE_YES = 1;
    public static final int IS_FILE_TYPE_ZIP = 2;

    /****
     *  0-非私人文件，1-私人文件
     */
    public static final int IS_PRIVATE_YES = 1;
    public static final int IS_PRIVATE_NO = 0;

    @Autowired
    private ItemService itemService;

    @Autowired
    private ProjectFileMapper projectFileMapper;

    @Autowired
    private ProjectFileLogService projectFileLogService;


    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FileUploadService fileUploadService;

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private BSNService bsnService;



    public List<ProjectFileDirVO> getProjectFileDirList(List<Integer> projectIds) {
        if (CollectionUtil.isEmpty(projectIds)) {
            return new ArrayList<>();
        }
        List<ProjectFileDirVO> projectDirs = projectFileMapper.getFileDirs(projectIds);
        return projectDirs;
    }


    /***
     * 获取当前登录用户关联的项目文件树列表
     * @param admin
     * @param isPrivate
     * @return
     */
    public ProjectFileDirVO fileList(Admin admin, Integer isPrivate) {
        //获取项目列表
        List<Item> list = itemService.getItemsByAdminId(admin.getId());
        if (isPrivate == IS_PRIVATE_NO) {

            if (CollectionUtil.isEmpty(list)) {
                return new ProjectFileDirVO();
            }
            //获取属于该项目的文件夹
            List<Integer> projectIds = list.stream().map(Item::getId).collect(Collectors.toList());
            List<ProjectFileDirVO> projectDirs = getProjectFileDirList(projectIds);
            ProjectFileDirVO treeList = handProjectFiles(list, projectDirs);
            return treeList;
        } else {
            //获取个人文件树列表
            //初始化顶级树
            ProjectFileDirVO topDir = new ProjectFileDirVO();
            topDir.setProjectId(0);
            topDir.setFileName("全部");
            topDir.setPid(-1);
            topDir.setId(0);
            topDir.setIsPrivate(IS_PRIVATE_YES);
            topDir.setCanEdit(false);
            List<ProjectFileDirVO> topList = new ArrayList<>();
            topList.add(topDir);
            //递归获取实际文件树目录
            List<ProjectFileDirVO> projectDirs = projectFileMapper.getPersonFileDirs(admin.getId());

            //获取属于该项目的文件夹
            List<Integer> projectIds = list.stream().map(Item::getId).collect(Collectors.toList());
            List<ProjectFileDirVO> itemDirs = getProjectFileDirList(projectIds);
            //获取创建的文件的文件夹项目
            List<ProjectFileDirVO> projectFileDirs = projectFileMapper.getPersonFiles(admin.getId());
            if (projectDirs == null) {
                projectDirs = new ArrayList<>();
            }
            if (CollectionUtil.isNotEmpty(projectFileDirs)) {
                projectDirs.addAll(projectFileDirs);
            }
            if (CollectionUtil.isNotEmpty(itemDirs)) {
                projectDirs.addAll(itemDirs);
            }
            List<ProjectFileDirVO> treeList = getTreeList(projectDirs, topList);
//            return treeList.get(0);
//            ProjectFileDirVO
            return myFileList(admin);
        }
    }

    /***
     * 处理文件夹树状结构
     * @param list
     * @param projectDirs
     * @return
     */
    public ProjectFileDirVO handProjectFiles(List<Item> list, List<ProjectFileDirVO> projectDirs) {
        //初始化顶级树
        ProjectFileDirVO topDir = new ProjectFileDirVO();
        topDir.setProjectId(0);
        topDir.setFileName("全部");
        topDir.setPid(-2);
        topDir.setId(-1);
        topDir.setCanEdit(false);

        //初始化二级树
        List<ProjectFileDirVO> projectDir = new ArrayList<>();
        list.stream().forEach(item -> {
            if (item != null) {
                ProjectFileDirVO fileDirVO = new ProjectFileDirVO();
                fileDirVO.setProjectId(item.getId());
                fileDirVO.setFileName(item.getName());
                fileDirVO.setId(0);
                fileDirVO.setPid(-1);
                fileDirVO.setCanEdit(false);
                projectDir.add(fileDirVO);
            }
        });

        //递归获取实际文件树目录
        List<ProjectFileDirVO> treeList = getTreeList(projectDirs, projectDir);
        topDir.setChildren(treeList);
        return topDir;
    }

    /***
     * 获取树目录
     *
     * @param dirs
     * @param dirs
     * @return
     */
    private List<ProjectFileDirVO> getTreeList(List<ProjectFileDirVO> dirs, List<ProjectFileDirVO> topList) {
        //获取一级目录
        topList.stream().forEach(topItem -> {
            List<ProjectFileDirVO> childrenItems = new ArrayList<>();
            dirs.stream().forEach(childItem -> {
                if (childItem.getIsPrivate() == IS_PRIVATE_NO) {
                    if (childItem.getPid().equals(topItem.getId()) && childItem.getProjectId().equals(topItem.getProjectId())) {
                        childrenItems.add(childItem);
                    }
                } else {
                    if (childItem.getPid().equals(topItem.getId())) {
                        childrenItems.add(childItem);
                    }
                }
            });
            topItem.setChildren(childrenItems);
            if (CollectionUtil.isNotEmpty(childrenItems)) {
                getTreeList(dirs, childrenItems);
            }
        });
        return topList;
    }

    /***
     * 验证是否有项目文件夹删除权限
     * @param admin
     * @param projectFile
     * @return
     */
    private boolean hasFileDeletePermission(Admin admin, ProjectFile projectFile) {

        //创建人自己可以删除
        if (admin.getId() != null && admin.getId().equals(projectFile.getCreatedBy())) {
            return true;
        }
        //权限验证
        if (!adminPermissionService.hasPermission(admin.getId(), "delete_project_file")) {
            throw new CustomParameterizedException("您没有操作权限");
        }
        return true;
    }


    /***
     * 我的文件列树表
     * @param admin
     * @return
     */
    public ProjectFileDirVO myFileList(Admin admin) {
        //获取收藏的列表  区分文件于文件夹

        //获取项目列表
        List<Item> itemList = itemService.getItemsByAdminId(admin.getId());
        if (CollectionUtil.isEmpty(itemList)) {
            return new ProjectFileDirVO();
        }

        Map<Integer, Item> itemMap = new HashMap<>();
        //获取属于该项目的文件夹
        List<Integer> projectIds = itemList.stream().map(item -> {
                    itemMap.put(item.getId(), item);
                    return item.getId();
                }
        ).collect(Collectors.toList());
        List<ProjectFileDirVO> projectDirs = this.getProjectFileDirList(projectIds);

        Map<Integer, ProjectFileDirVO> dirMap = new HashMap<>();
//        if (CollectionUtil.isEmpty(projectDirs)) {
//            return new ProjectFileDirVO();
//        }
        projectDirs.stream().forEach(dir -> {
            dirMap.put(dir.getId(), dir);
        });

        //获取我的文件列表
        List<ProjectFileDirVO> list = baseMapper.getMyFiles(admin.getId());

        if (CollectionUtil.isEmpty(list)) {
            return new ProjectFileDirVO();
        }

        //过滤文件树 显示收藏相关的文件树
        Set<Integer> fileIds = new HashSet<>();
        List<Integer> realProjectIds = new ArrayList<>();

        list.stream().forEach(collect -> {
            Integer type = collect.getType();

            if (type != null) {
                realProjectIds.add(collect.getProjectId());
                //是文件夹，则对于子文件拥有所有权限
                if (type == ProjectFileService.IS_FILE_TYPE_NO) {
                    //遍历顶部
                    collectService.getCurTopList(dirMap.get(collect.getId()), dirMap, fileIds);
                } else {
                    //如果是文件则获取他的上级文件夹id
                    //遍历顶部
                    collectService.getCurTopList(dirMap.get(collect.getPid()), dirMap, fileIds);
                }
            }
        });

        //记录所属的项目id
        List<Integer> itemIds = new ArrayList<>();
        List<Item> realItem = new ArrayList<>();

        //获取实际的项目文件目录
        List<ProjectFileDirVO> expandList = new ArrayList<>();
        realProjectIds.stream().forEach(itemId -> {
            if (!itemIds.contains(itemId)) {
                itemIds.add(itemId);
                Item r_item = itemMap.get(itemId);
                if (r_item != null) {
                    realItem.add(r_item);
                }
            }
        });

        fileIds.stream().forEach(id -> {
            ProjectFileDirVO vo = dirMap.get(id);
            if (vo != null) {
                expandList.add(vo);
            }
        });
        ProjectFileDirVO treeList = this.handProjectFiles(realItem, expandList);
        return treeList;
    }

    /***
     * 验证是否有项目文件夹创建于重命名权限
     * @param admin
     * @return
     */
    private boolean hasFileCreatePermission(Admin admin) {
        if (admin.getHasCreateFile()) {
            return true;
        }
        //权限验证
        if (!adminPermissionService.hasPermission(admin.getId(), "mkdir_project_file")) {
            throw new CustomParameterizedException("您没有操作权限");
        }
        return true;
    }

    /***
     * 是否有全局搜索权限
     * @param admin
     * @return
     */
    private boolean hasSearchGlob(Admin admin) {
        //权限验证
        return adminPermissionService.hasPermission(admin.getId(), "search_project_file");
    }


    /***
     * 检验文件名是否已经存在
     * @param fileName
     */
    private void checkFileName(String fileName, Integer pid, Integer projectId) {
        if (StringUtils.isEmpty(fileName)) {
            throw new CustomParameterizedException("文件名称不能为空!");
        }

        //判断同一层级的文件名称不能重复
        LambdaQueryWrapper<ProjectFile> query = new LambdaQueryWrapper<>();
        if (projectId != null) {
            query.eq(ProjectFile::getProjectId, projectId);
        }
        query.eq(ProjectFile::getPid, pid);
        query.eq(ProjectFile::getFileName, fileName.trim());
        if (CollectionUtil.isNotEmpty(this.list(query))) {
            throw new CustomParameterizedException("该文件名\"" + fileName + "\"已经存在！");
        }

    }

    /***
     * 创建文件夹
     * @param admin
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity createFile(Admin admin, ProjectFileDirDTO dto) {
        ProjectFile projectFile = createRealFile(admin, dto);
        return ResponseEntity.ok(true);
    }

    public ProjectFile createRealFile(Admin admin, ProjectFileDirDTO dto) {
        if (dto.getIsPrivate() == IS_PRIVATE_NO && dto.getPid() != null && dto.getPid() == 0) {
            //TODO 判断是否是项目经理权限 有创建文件夹的权限
            hasFileCreatePermission(admin);
        }

        //判断文件名称是否存在
        checkFileName(dto.getFileName(), dto.getPid(), dto.getProjectId());
        ProjectFile projectFile = ProjectFile.builder()
                .projectId(dto.getProjectId())
                .pid(dto.getPid())
                .fileName(dto.getFileName())
                .isDelete(0)
                .isPrivate(dto.getIsPrivate())
                .hash(getHash(""))
                .type(dto.getType())
                .createdTime(new Date())
                .createdBy(admin.getId())
                .createName(admin.getNickName())
                .updatedTime(new Date())
                .versionId(RandomUtil.randomStringUpper(32))
                .status("Normal")
                .fileType(dto.getFileType())
                .updatedName(admin.getNickName())
                .updatedBy(admin.getId())
                .updatedTime(new Date())
                .build();

        if (dto.getType() == IS_FILE_TYPE_YES) {

            if (StringUtils.isEmpty(dto.getFileName())) {
                throw new CustomParameterizedException("文件名称不能为空");
            }
            String extName = dto.getFileName().substring(dto.getFileName().lastIndexOf(".") + 1);
            //判断该文件是否支持创建
            if (!fileUploadService.checkFileExtension(extName)) {
                throw new CustomParameterizedException("暂不支持该类型的文件!");
            }

            String fillDirName = DateUtil.format(new Date(), "yyyyMMdd");
            try {   //创建文件
                String uploadName = String.format("%d_%s.%s", System.currentTimeMillis(), RandomUtil.randomString(5), extName);
                String path = "/" + fillDirName + "/" + uploadName;
                File file = new File(filePath + path);
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                if (!file.exists()) {
                    createOfficeFile(file);
                }
                projectFile.setAllPath(file.getPath());
                projectFile.setExtName(extName);
                projectFile.setHash(getHash(file.getAbsolutePath()));
                projectFile.setFileMd5(bsnService.getFileMd5(file.getAbsolutePath()));
                projectFile.setPath(path);
                projectFile.setSize(file.length());
            } catch (Exception e) {
                log.error("创建文件错误", e);
            }
        }
        boolean flag = this.save(projectFile);
        if (flag) {
            //添加操作日志
            projectFileLogService.saveLog(admin, projectFile.getId(), ProjectFileLogType.ADD.name());
        }
        return projectFile;
    }


    /***
     * 创建office文件
     * @param file
     */
    private void createOfficeFile(File file) {
        String fileName = file.getName();
        String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
        //获取模板文件
        StringBuilder sb = new StringBuilder();
        sb.append(filePath)
                .append("/officeTemplate/simple_1.")
                .append(extName);
        try {
            versionService.copyFile(sb.toString(), file.getPath());
        } catch (Exception e) {
            log.error("模板文件不存在", e);
            throw new CustomParameterizedException("暂不支持创建该格式的文件");
        }
    }

    /***
     * hash值获取目前 未确定使用哪一部分
     * @return
     */
    public String getHash(String path) {
        if(StringUtils.isEmpty(path)){
            return RandomUtil.randomString(64);
        }
        try {
            return DigestUtil.sha256Hex(new File(path));
        }catch (Exception e){
            return RandomUtil.randomString(64);
        }
    }

    /***
     * 重命名文件或者文件夹
     * @param id
     * @param fileName
     */
    @Transactional(rollbackFor = Exception.class)
    public void fileRename(Admin admin, Integer id, String fileName) {


        ProjectFile projectFile = this.getById(id);
        if (projectFile == null) {
            throw new CustomParameterizedException("该文件已不存在");
        }

        if (projectFile.getFileType() != null && projectFile.getFileType().equals(1)) {
            throw new CustomParameterizedException("系统文件不能重命名");
        }
        //判断文件名称是否存在
        checkFileName(fileName, projectFile.getPid(), projectFile.getProjectId());

        if (projectFile.getIsPrivate() == IS_PRIVATE_NO && projectFile.getType() == IS_FILE_TYPE_NO) {
            //TODO 判断用户是否有操作权限
            hasFileCreatePermission(admin);
        }
        projectFile.setFileName(fileName);
        projectFile.setUpdatedTime(new Date());
        projectFile.setUpdatedBy(admin.getId());
        projectFile.setUpdatedName(admin.getNickName());
        if (this.updateById(projectFile)) {
            //记录操作日志
            projectFileLogService.saveLog(admin, id, ProjectFileLogType.RENAME.name());
        }
    }


    /***
     * 批量删除文件
     * @param admin
     * @param ids
     */
    public void batchDetailFile(Admin admin, String ids) {
        if (StringUtils.isEmpty(ids)) {
            throw new CustomParameterizedException("请指定要删除的文件!");
        }
        if (ids.contains(",")) {
            String[] idList = ids.split(",");
            Arrays.stream(idList).forEach(id -> {
                deleteFile(admin, Integer.valueOf(id));
            });
        } else {
            deleteFile(admin, Integer.valueOf(ids));
        }
    }

    /***
     * 删除指定文件
     * @param admin
     * @param id
     */
    public void deleteFile(Admin admin, Integer id) {
        //TODO 判断用户权限，能否删除
        ProjectFile projectFile = this.getById(id);
        if (projectFile == null) {
            throw new CustomParameterizedException("该文件已不存在");
        }
        //说明是文件夹
        if (projectFile.getType() == IS_FILE_TYPE_NO) {
            //判断是否有删除权限
            hasFileDeletePermission(admin, projectFile);
            //判断有没有子文件 有子文件则不能删除
            LambdaQueryWrapper<ProjectFile> query = new LambdaQueryWrapper<>();
            query.eq(ProjectFile::getPid, id);
            List<ProjectFile> list = this.list(query);
            if (CollectionUtil.isNotEmpty(list)) {
                //删除所有子文件
                this.removeByIds(list.stream().map(ProjectFile::getId).collect(Collectors.toList()));
                //删除实际存在的温江
                list.stream().forEach(vo -> {
                    File f = new File(filePath + vo.getPath());
                    if (f.exists()) {
                        f.delete();
                    }
                });
            }
        }

        if (this.removeById(projectFile)) {
            //保留历史版本
            versionService.copyVersion(projectFile, admin);

            //删除实际存在的文件
            File file = new File(filePath + projectFile.getPath());
            if (file.exists()) {
                file.delete();
            }
            //记录操作日志
            projectFileLogService.saveLog(admin, id, ProjectFileLogType.DELETE.name());
        }
    }

    /***
     * 根军父级id获取其子文件或者文件夹(获取一级)
     *
     * @param admin
     * @param pId
     * @param page
     * @param pageSize
     * @param fileName
     * @return
     */
    public PagerVO<ProjectFileVO> getFilesByPId(Admin admin, Integer pId, int page, int pageSize, Integer projectId, String fileName, Integer isPrivate) {
        if ((projectId == null || projectId == 0) && (pId == null || pId < 0)) {
            throw new CustomParameterizedException("请输入正确的父级id");
        }
        //获取当前用户收藏列表
        //获取收藏的文件夹
        LambdaQueryWrapper<ProjectFileCollect> query = new LambdaQueryWrapper<>();
        query.eq(ProjectFileCollect::getCreatedBy, admin.getId());
        List<ProjectFileCollect> list = collectService.list(query);

        List<Integer> collectFileIds = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(list)) {
            collectFileIds = list.stream().map(ProjectFileCollect::getFileId).collect(Collectors.toList());
        }

        Page<ProjectFileVO> pagerVO = PageHelper.startPage(page, pageSize).doSelectPage(() -> projectFileMapper.getFilesByPId(pId, projectId, fileName, isPrivate, admin.getId()));
        List<Integer> finalCollectFileIds = collectFileIds;
        pagerVO.getResult().stream().forEach(vo -> {
            if (finalCollectFileIds.contains(vo.getId())) {
                vo.setIsCollect(true);
            }
            if (!StringUtils.isEmpty(vo.getSize())) {
                vo.setSize(FileUtil.readableFileSize(Long.valueOf(vo.getSize())));
            }
            if (IS_FILE_TYPE_YES == vo.getType()) {
                //判断文件是否在监听中
                vo.setStatus(officeFileService.userIsEdit(admin, vo.getId()));
            }

        });
        return PagerVO.of(pagerVO);
    }

    /***
     * 保存文件
     * @param admin
     * @param fileVOS
     * @param pId
     * @param projectId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity saveFile(Admin admin, List<FileVO> fileVOS, Integer pId, int projectId) {
        //保存文件
        saveRealFile(admin, fileVOS, pId, projectId);
        return ResponseEntity.ok(true);
    }

    /***
     * 保存上传文件
     * @param admin
     * @param fileVOS
     * @param pId
     * @param projectId
     * @return
     */
    public List<ProjectFile> saveRealFile(Admin admin, List<FileVO> fileVOS, Integer pId, int projectId) {
        //保存文件
        ProjectFile projectFile = this.getById(pId);
        if (projectFile == null && pId > 0) {
            throw new CustomParameterizedException("该文件已不存在");
        }
        if (projectFile == null) {
            projectFile = new ProjectFile();
            projectFile.setProjectId(projectId);
        }
        List<ProjectFile> list = new ArrayList<>();
        ProjectFile finalProjectFile = projectFile;
        fileVOS.stream().forEach(fileVO -> {
            ProjectFile file = ProjectFile.builder()
                    .projectId(finalProjectFile.getProjectId())
                    .pid(pId)
                    .fileName(fileVO.getOriginName())
                    .isDelete(0)
                    .isPrivate(finalProjectFile.getIsPrivate())
                    .hash(getHash(filePath + fileVO.getPath()))
                    .size(fileVO.getSize())
                    .fileMd5(bsnService.getFileMd5(filePath + fileVO.getPath()))
                    .path(fileVO.getPath())
                    .allPath(fileVO.getFullUrl())
                    .extName(fileVO.getExtension())
                    .type(IS_FILE_TYPE_YES)
                    .createdTime(new Date())
                    .createdBy(admin.getId())
                    .createName(admin.getNickName())
                    .updatedBy(admin.getId())
                    .updatedName(admin.getNickName())
                    .versionId(RandomUtil.randomStringUpper(16))
                    .status("Normal")
                    .updatedTime(new Date())
                    .build();
            checkFileName(file.getFileName(), pId, projectId);
            this.save(file);
            list.add(file);
            //保存日志记录
            projectFileLogService.saveLog(admin, file.getId(), ProjectFileLogType.ADD.name());
        });
        //处理成功删除临时文件
        fileUploadService.deleteFileByFileVO(fileVOS, false, true);
        return list;
    }

    /***
     * 获取文件地址
     * @param admin
     * @param id
     * @return
     */
    public String getFile(Admin admin, Integer id) {

        return getWopiUrl(admin, id, "view");
    }


    /***
     * 获取文件路径
     * @param admin
     * @param id
     * @return
     */
    private String getWopiUrl(Admin admin, Integer id, String action) {
        //获取文件
        ProjectFile file = getById(id);
        if (file == null) {
            throw new CustomParameterizedException("当前文件不存在!");
        }
        if (file.getType() == IS_FILE_TYPE_NO) {
            throw new CustomParameterizedException("文件夹不能打开!");
        }

//        判断文件实际是否存在
        File realFile = new File(filePath + file.getPath());
        if (!realFile.exists()) {
            throw new CustomParameterizedException("该文件已不存在于文件系统!");
        }

        List<OfficeDetailVo> officeUrls = getOfficeList();
        String extName = file.getExtName();
        StringBuilder baseUrl = new StringBuilder();
        officeUrls.stream().forEach(office -> {
            String eName = office.getExtName();
            String eAction = office.getAction();
            if (extName.equals(eName) && action.equals(eAction)) {
                String tempUrl = office.getUrl().substring(0, office.getUrl().indexOf("<"));
                baseUrl.append(tempUrl);
                if (tempUrl.contains("?")) {
                    baseUrl.append("ui=zh-CN");
                } else {
                    baseUrl.append("?ui=zh-CN");
                }
            }
        });

        if (baseUrl.length() < 5) {
            throw new CustomParameterizedException("暂不支持该格式的文件!");
        }

        String name = file.getPath().trim();
        String userName = "宜通衡睿";//admin.getNickName();//
        StringBuilder sb = new StringBuilder();
        StringBuilder md5Key = new StringBuilder();
        try {
            String codeFileName = new String(Base64Utils.encode(name.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
            String codeUserName = new String(Base64Utils.encode(URLEncoder.encode(userName, "utf-8").getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
            //拼接验证key 用于判断是否是项目可用的请求
            md5Key.append(codeFileName)
                    .append(id);
            sb.append(wopiUrl)
                    .append(codeFileName)
                    .append("?userName=").append(codeUserName);
            String key = SecureUtil.md5(md5Key.append(digst).toString());
            sb.append("&action=").append(action);
            sb.append("&ownerId=").append("admin");
            sb.append("&fileId=").append(id);
            sb.append("&access_token=").append(key);
            sb.append("&access_token_ttl=").append(10000);
            String srcUrl = URLEncoder.encode(sb.toString(), "utf-8");
            return baseUrl.append("&WOPISrc=").append(srcUrl)
                    .toString();
        } catch (Exception e) {
            log.error("处理路径错误", e);
        }
        return "srcUrl";
    }

    /***
     * 编辑文件
     * @param admin
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String editFile(Admin admin, Integer id) {

        //TODO 进入编辑页面则需要保存一个历史版本
        //获取文件
        ProjectFile file = getById(id);
        if (file == null) {
            throw new CustomParameterizedException("当前文件不存在!");
        }
        if (file.getType() == IS_FILE_TYPE_NO) {
            throw new CustomParameterizedException("文件夹不能打开!");
        }
        file.setUpdatedBy(admin.getId());
        //获取文件大小
        File temp = new File(filePath + file.getPath());
        file.setSize(temp.length());
        file.setUpdatedName(admin.getNickName());
        file.setUpdatedTime(new Date());
        //记录写入编辑状态
        officeFileService.writeEditStatus(file.getId());
//        file.setStatus("Edit");
        this.updateById(file);
        versionService.copyVersion(file, admin);
        //保存操作历史记录
        //保存日志记录
        projectFileLogService.saveLog(admin, file.getId(), ProjectFileLogType.EDIT.name());
        return getWopiUrl(admin, id, "edit");
    }

    private List<OfficeDetailVo> getOfficeList() {
        String url = wopiServer + "/hosting/discovery";
        String xml = restTemplate.getForObject(url, String.class);
        //解析xml
        JSONObject temp = XML.toJSONObject(xml);
        JSONObject wopidiscovery = temp.getJSONObject("wopi-discovery");
        JSONObject netzone = wopidiscovery.getJSONObject("net-zone");
        JSONArray array = netzone.getJSONArray("app");
        //遍历各节点数据
        List<OfficeVo> list = new ArrayList<>();
        List<OfficeDetailVo> detailList = new ArrayList<>();
        array.stream().forEach(appObj -> {
            JSONObject obj = (JSONObject) appObj;
            OfficeVo vo = new OfficeVo();
            vo.setIcon(obj.getStr("favIconUrl"));
            vo.setName(obj.getStr("name"));
            JSONArray actions = obj.getJSONArray("action");
            List<OfficeDetailVo> details = new ArrayList<>();
            actions.stream().forEach(actionObj -> {
                JSONObject action = (JSONObject) actionObj;
                OfficeDetailVo detailVo = new OfficeDetailVo();
                detailVo.setUrl(action.getStr("urlsrc"));
                detailVo.setAction(action.getStr("name"));
                detailVo.setExtName(action.getStr("ext"));
                details.add(detailVo);
                detailList.add(detailVo);
            });
            vo.setDetails(details);
            list.add(vo);
        });
        return detailList;
    }

    public String getOfficeUrl() {
//        List<OfficeVo> list = getOfficeList();
//        return JSON.toJSONString(list);
        return null;
    }

    /***
     * 搜索文件，保存简单搜索，高级搜索
     *
     * @param admin
     * @param searchFileDto
     * @return
     */
    public PagerVO<ProjectFileVO> searchFile(Admin admin, SearchFileDto searchFileDto) {
        //获取当前用户收藏列表
        //获取收藏的文件夹
        List<Integer> collectFileIds = new ArrayList<>();
        if (admin != null) {
            LambdaQueryWrapper<ProjectFileCollect> query = new LambdaQueryWrapper<>();
            query.eq(ProjectFileCollect::getCreatedBy, admin.getId());
            List<ProjectFileCollect> list = collectService.list(query);
            if (CollectionUtil.isNotEmpty(list)) {
                collectFileIds = list.stream().map(ProjectFileCollect::getFileId).collect(Collectors.toList());
            }
        }

        //判断当前用户是否有全局搜索权限
        searchFileDto.setSearchGlob(hasSearchGlob(admin));
        if (!searchFileDto.getSearchGlob()) {
            //获取项目id 列表
            //获取项目列表
            List<Item> list = itemService.getItemsByAdminId(admin.getId());
            if (CollectionUtil.isNotEmpty(list)) {
                searchFileDto.setProjectIds(list.stream().map(Item::getId).collect(Collectors.toList()));
            } else {
                throw new CustomParameterizedException("您没有文件权限!");
            }
        }

        Page<ProjectFileVO> page = PageHelper.startPage(searchFileDto.getPage(), searchFileDto.getPageSize())
                .doSelectPage(() -> projectFileMapper.searchFile(searchFileDto));
        List<Integer> finalCollectFileIds = collectFileIds;
        page.getResult().stream().forEach(vo -> {
            if (finalCollectFileIds.contains(vo.getId())) {
                vo.setIsCollect(true);
            }
        });
        return PagerVO.of(page);
    }

    /***
     * 获取所有更改人数据
     * @return
     */
    public List<UpdateDTO> getUpdateList() {

        return projectFileMapper.getUpdateList();
    }

    public ProjectDetail projectFileDetail(Admin admin, Integer fId, int projectId) {
        ProjectDetail projectDetail = new ProjectDetail();

        if (fId == null || fId == 0) {
            throw new CustomParameterizedException("系统文件不能查看属性");
        }

        //获取项目详情
        ProjectFile file = getById(fId);
        if (file == null) {
            throw new CustomParameterizedException("当前文件已不存在!");
        }
        ProjectFileVO fileVO = new ProjectFileVO();
        BeanUtils.copyProperties(file, fileVO);
        fileVO.setSize(FileUtil.readableFileSize(file.getSize()));
        //获取历史版本
        List<ProjectFileVersion> versions = versionService.getList(fId);

        //获取日志
        List<ProjectFileLog> logs = projectFileLogService.getList(fId);

        projectDetail.setFileVO(fileVO);
        projectDetail.setVersions(versions);
        projectDetail.setLogs(logs);
        return projectDetail;
    }


    /***
     * 文本方式查看文件
     * @param admin
     * @param id
     * @return
     */
    public String viewText(Admin admin, Integer id) {
        //获取项目详情
        ProjectFile file = getById(id);
        if (file == null) {
            throw new CustomParameterizedException("当前文件已不存在!");
        }
        if (file.getType() == IS_FILE_TYPE_NO) {
            throw new CustomParameterizedException("文件夹无法预览!");
        }

        //判断文件实际是否存在
        File realFile = new File(filePath + file.getPath());
        if (!realFile.exists()) {
            throw new CustomParameterizedException("该文件已不存在于文件系统!");
        }
        //读取文件内容
        String extName = file.getExtName();
        String realPath = filePath + file.getPath();
        String result = "";
        try {
            if ("doc".equals(extName) || "docx".equals(extName)) {
                Word07Writer word07Writer = new Word07Writer(new File(realPath));
                XWPFWordExtractor wordExtractor = new XWPFWordExtractor(word07Writer.getDoc());
                result = wordExtractor.getText();
            } else if ("xls".equals(extName) || "xlsx".equals(extName)) {
                ExcelReader excelReader = ExcelUtil.getReader(new FileInputStream(realPath));
                List<List<Object>> listAll = excelReader.read(0, excelReader.getRowCount());
                StringBuilder sb = new StringBuilder();
                listAll.stream().forEach(temp -> {
                    temp.stream().forEach(str -> {
                        if (str == null) {
                            str = "";
                        }
                        sb.append(str).append("\t");
                    });
                    sb.append("\n");
                });
                result = sb.toString();
            } else if ("txt".equals(extName)) {
                FileReader fileReader = new FileReader(realPath);
                result = URLDecoder.decode(fileReader.readString(), "utf-8");

            } else {
                throw new CustomParameterizedException("暂不支持该文件预览!");
            }
        } catch (Exception e) {
            log.error("预览文件失败", e);
            throw new CustomParameterizedException("预览文件失败");
        }
        return result;
    }

    /**
     * 保存文本内容
     *
     * @param admin
     * @param fId
     * @param content
     */
    public void saveTxtFile(Admin admin, Integer fId, SaveFileDTO content) {
        //获取项目详情
        ProjectFile file = getById(fId);
        if (file == null) {
            throw new CustomParameterizedException("当前文件已不存在!");
        }
        if (file.getType() == IS_FILE_TYPE_NO) {
            throw new CustomParameterizedException("文件夹无法编辑!");
        }
        String realPath = filePath + file.getPath();
        FileWriter fileWriter = new FileWriter(realPath);
        fileWriter.write(content.getContent());
    }

    /***
     * 下载文件
     * @param fId
     * @param response
     */
    public void downloadFile(Integer fId, HttpServletResponse response) {
        //获取项目详情
        ProjectFile file = getById(fId);
        if (file == null) {
            throw new CustomParameterizedException("当前文件已不存在!");
        }
        if (file.getType() == IS_FILE_TYPE_NO) {
            throw new CustomParameterizedException("文件夹暂时不提供打包下载!");
        }

        String path = file.getPath();
        String fileName = file.getFileName();
        if (!StringUtils.isEmpty(path) && !StringUtils.isEmpty(fileName)) {
            File downloadFile = new File(filePath + file.getPath());
            fileUploadService.getFile(downloadFile, response, fileName);
        } else {
            throw new CustomParameterizedException("获取不到文件路径");
        }
    }

    /***
     * 批量下载文件
     * @param fileDownloadDTOS
     * @param response
     */
    public void downloads(Admin admin, FileDownloadDTO fileDownloadDTOS, HttpServletResponse response) {
        //创建文件包 打包下载
        if (fileDownloadDTOS == null || CollectionUtil.isEmpty(fileDownloadDTOS.getFIds())) {
            throw new CustomParameterizedException("请选择要下载的文件");
        }
        //批量获取文件实体
        List<Integer> fids = fileDownloadDTOS.getFIds();
        if (StringUtils.isEmpty(fids)) {
            throw new CustomParameterizedException("请先选中文件!");
        }
        LambdaQueryWrapper<ProjectFile> query = new LambdaQueryWrapper<>();
        query.in(ProjectFile::getId, fids);
        List<ProjectFile> projectFiles = this.list(query);
        if (CollectionUtil.isEmpty(projectFiles)) {
            throw new CustomParameterizedException("获取不到文件，请稍后重试!");
        }
        //创建临时文件夹-> 按照对应的目录copy文件->打包-》提供下载地址
        String downLoadName = String.format("文件下载_%s.zip", RandomUtil.randomString(8));
        String temDir = RandomUtil.randomString(6);
        File zipDir = new File(filePath + "/zipout/" + temDir + "/" + downLoadName);
        if (!zipDir.getParentFile().exists()) {
            zipDir.getParentFile().mkdirs();
        }
        List<FileVO> list = new ArrayList<>();
        getFiles(list, projectFiles, null, zipDir.getParentFile().getPath());
        File file = cn.hutool.core.util.ZipUtil.zip(zipDir, true, zipDir.getParentFile().listFiles());
        fileUploadService.getFile(file, response, downLoadName);
        FileUtil.del(file.getParentFile());
    }

    /***
     * 批量获取文件
     * @param list
     * @param projectFiles
     * @param  curPath 当前文件位置
     * @param  dirName  上一级文件位置
     * @return
     */
    private List<FileVO> getFiles(List<FileVO> list, List<ProjectFile> projectFiles, String dirName, String curPath) {
        if (CollectionUtil.isNotEmpty(projectFiles)) {
            if (dirName != null) {
                curPath = curPath + "/" + dirName;
            }
            String finalCurPath = curPath;
            File temp = new File(finalCurPath);
            if (!temp.exists()) {
                temp.mkdirs();
            }
            projectFiles.stream().forEach((file) -> {
                if (IS_FILE_TYPE_YES == file.getType()) {
                    FileVO vo = new FileVO();
                    vo.setPath(file.getPath().substring(1));
                    vo.setUrl(vo.getPath());
                    vo.setSize(file.getSize());
                    vo.setName(file.getFileName());
                    vo.setOriginName(file.getFileName());
                    vo.setExtension(file.getExtName());
                    //复制文件到指定文件夹
                    File f1 = new File(filePath + file.getPath());
                    if (f1.exists()) {
                        FileUtil.copyFile(f1.getPath(), finalCurPath + "/" + file.getFileName());
                    }
                    list.add(vo);
                } else {
                    //获取子文件
                    LambdaQueryWrapper<ProjectFile> query = new LambdaQueryWrapper<>();
                    query.eq(ProjectFile::getPid, file.getId());
                    List<ProjectFile> tempList = this.list(query);
                    if (CollectionUtil.isNotEmpty(tempList)) {
                        getFiles(list, tempList, file.getFileName(), finalCurPath);
                    }
                }
            });
        }
        return list;
    }

    public static void main(String[] args) {
//        Class clazz = IapDataModelBaseVO.class;
//        Field[] fields = clazz.getDeclaredFields();
//        StringBuilder sb = new StringBuilder();
//        for (Field fid : fields) {
//            String name = fid.getName();
//            ApiModelProperty apiModelProperty = fid.getAnnotation(ApiModelProperty.class);
//            String des = "";
//            if (apiModelProperty != null) {
//                des = apiModelProperty.value();
//            }
//            sb.append(name).append("(").append(des).append(")").append("、");
//        }
//        System.out.println(sb.toString());
//        Admin admin  =new Admin();
//        admin.setNickName("jiayen");
//        admin.setAccount("jiayen");
//        admin.setCompanyName("测试");
//
//        AdminVO adminVO = new AdminVO();
//        adminVO.setId(1);
//        BeanUtils.copyProperties(adminVO,admin);
//        System.out.println(JSON.toJSON(admin));
//        MD5 md5 = new MD5("ffff".getBytes(StandardCharsets.UTF_8),4);
//        String aa =md5.digestHex("1qaz2wsx");
//        System.out.println(aa);
        Date sTime = DateUtil.parseDate("2021-05-04");
        Date eTime = DateUtil.parseDate("2021-06-23");
        System.out.println(DateUtil.between(sTime, eTime, DateUnit.DAY));
    }

    /***
     * 获取文档类型 执行打开
     * @param admin
     * @param fId
     * @param request
     * @return
     */
    public FileMode getFileMode(Admin admin, Integer fId, HttpServletRequest request) {
        ProjectFile projectFile = getById(fId);
        if (projectFile == null) {
            throw new CustomParameterizedException("文件不存在");
        }
        String hostPath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
        String officeHost = officeUrl;
        String chageUrl = String.format("%s/projectFile/openFIle/forceSave", hostPath);
        String url = String.format("%s/projectFile/download/%s", hostPath, projectFile.getId());
        FileMode fileMode = FileMode.builder()
                .changesurl(chageUrl)
                .fileType(projectFile.getExtName())
                .officeHost(officeHost)
                .url(url)
                .fileName(projectFile.getFileName())
                .key(DigestUtil.md5Hex(projectFile.getId() + projectFile.getFileName()))
                .build();
        return fileMode;
    }

    /***
     * 文档更改版本回调地址
     * @param request
     * @param response
     * @return
     */
    public void changeUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {

        PrintWriter writer = response.getWriter();

        Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
        String body = scanner.hasNext() ? scanner.next() : "";

        com.alibaba.fastjson.JSONObject jsonObj = (com.alibaba.fastjson.JSONObject) JSON.parse(body);

        if ((long) jsonObj.get("status") == 2) {
            String downloadUri = jsonObj.getString("url");

            String fileName = jsonObj.getString("");
            URL url = new URL(downloadUri);
            java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
            InputStream stream = connection.getInputStream();

            File savedFile = new File(filePath);
            try (FileOutputStream out = new FileOutputStream(savedFile)) {
                int read;
                final byte[] bytes = new byte[1024];
                while ((read = stream.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }
                out.flush();
            }
            connection.disconnect();
        }
        writer.write("{\"error\":0}");

    }


    /***
     * 获取需求单或者确认单 文件夹
     * @param projectId
     * @param fileName 文件夹名称
     * @return
     */
    public ProjectFile getCerDir(Integer projectId, String fileName, Admin admin, Integer parentId) {
        //获取当前登录用户
//        Admin admin = cu
        LambdaQueryWrapper<ProjectFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProjectFile::getProjectId, projectId)
                .eq(ProjectFile::getFileName, fileName)
                .eq(ProjectFile::getType, 0)
                .eq(ProjectFile::getPid, parentId)
                .eq(ProjectFile::getFileType, 1);
        ProjectFile projectFile = this.getOne(queryWrapper);
        if (projectFile == null) {
            // 获取当前登录用户
            //初始化该文件夹
            ProjectFileDirDTO dto = new ProjectFileDirDTO();
            dto.setFileName(fileName);
            dto.setProjectId(projectId);
            dto.setType(0);
            dto.setFileType(1);
            dto.setPid(parentId);
            projectFile = createRealFile(admin, dto);
        }
        return projectFile;
    }

    /***
     * 获取需求单或者确认单 文件夹
     * @param projectId
     * @param cerType
     * @return
     */
    public ProjectFile getCerDir(Integer projectId, Integer cerType, Admin admin) {
        //获取当前登录用户
//        Admin admin = cu
        LambdaQueryWrapper<ProjectFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProjectFile::getProjectId, projectId)
                .eq(ProjectFile::getFileName, getCerName(cerType))
                .eq(ProjectFile::getType, 0)
                .eq(ProjectFile::getFileType, 1);
        ProjectFile projectFile = this.getOne(queryWrapper);
        if (projectFile == null) {
            // 获取当前登录用户
            //初始化该文件夹
            ProjectFileDirDTO dto = new ProjectFileDirDTO();
            dto.setFileName(getCerName(cerType));
            dto.setProjectId(projectId);
            dto.setType(0);
            dto.setFileType(1);
            dto.setPid(0);
            projectFile = createRealFile(admin, dto);
        }
        return projectFile;
    }

    private String getCerName(Integer cerType) {
        if (CertificateFile.NEED_TYPE == cerType) {
            //需求单  获取需求单文件夹，没有则创建
            return "需求单";
        } else if (CertificateFile.CONFIRM_TYPE == cerType) {
            //确认单  获取确认单文件夹，没有则创建
            return "确认单";
        } else {
            throw new CustomParameterizedException("暂不支持该单类型");
        }
    }
}
